The NIH Image macro language has the standard set of pascal loops. This includes "for" loops and "while" loops. Although close to pascal, the macro language doesn't have everything as this email shows:
From wayne@helix.nih.gov (Wayne Rasband) reply on nih-image@soils.umn.edu
>in the "for" command (as in for i= 1 to fred Do) is there a skip command.
>For example, can I choose to do:
> for i = 1 to fred by 10 DO
The NIH Image macro language is (almost) a subset of Pascal and the Pascal FOR statement does not have a BY option. Instead, use a WHILE loop. For example:
i:=1;
while i<=fred do begin
{process}
i:=i+10;
end;
Before you start looking at macro ROI's an introduction to coordinates is worthwhile. See the picture below for a general guideline. Regions of interest are characterized by 'marching ants' which surround a selection.

Getting ROI information
GetRoi(left,top,width,height)
You will want to call this macro routine if you need any information about the current ROI. The routine returns a width of zero if no ROI exists.
ROI creation
SelectAll
The Selectall macro command is equivalent to the Pascal SelectAll(true), which selects all of the image and shows the ROI's 'marching ants'. See the above paragraph for pascal code relating to Selectall.
MakeRoi(left,top,width,height)
This is as straight forward as the name implies.
MakeOvalRoi(left,top,width,height)
Not terribly differing to implement from MakeROI. If you want a circular ROI set width and height to the same value. See the example below.
Altering an existing ROI
MoveRoi(dx,dy)
Use to move right dx and down dy.
InsetRoi(delta)
Expands the ROI if delta is negative, Shrinks the ROI if delta is positive.
Other routines involving ROI's
RestoreROI,KillRoi
These are opposities.
Copy,Paste,Clear,Fill,Invert,DrawBoundary
The example below shows a macro which operates until the mouse button is pressed. Button is your basic true or false boolean and becomes true when the button is pressed.
macro 'Show RGB Values [S]';
var
x,y,v,savex,savey:integer;
begin
repeat
savex:=x; savey:=y;
GetMouse(x,y);
if (x<>savex) or (y<>savey) then begin
v:=GetPixel(x,y);
ShowMessage('loc=',x:1,', ',y:1,
'\value=',v:1,
'\RGB=',RedLUT[v]:1,', ',GreenLUT[v]:1,', ',BlueLUT[v]:1);
wait(.5);
end;
until button;
end;
The macro "KeyDown(key)" (Key = 'option', 'shift', or 'control') returns a boolean true or false. It returns TRUE if the specified key is down. The example macro below can be run on any stack, using shift to delay more or control to delay less.
macro 'Animate Stack';
var
i,delay:integer;
begin
RequiresVersion(1.56);
i:=0;
delay:=0.1;
repeat
i:=i+1;
if i>nSlices then i:=1;
Wait(delay);
SelectSlice(i);
if
KeyDown('shift')
then delay:=1.5*delay;
if delay>1 then delay:=1;
if
KeyDown('control')
then delay:=0.66*delay;
if
KeyDown('option')
then beep;
ShowMessage('delay=',delay:4:2);
until button;
end;
There are a number of arrays in macros, but there are two varieties the measurement arrays and the rUser arrays. You can store macro data and results in the rUser arrays. These arrays are not affected by the Measurement counter (rCount) which works with measurements arrays such as rMean[rCount], rArea, etc. The current rCount for these is changed by doing a measurement or calling SetCounter.
Example of storing data to the rUser arrays:
rUser1[1]:=SomeNumber;
rUser2[1]:=SomeOtherNumber;
If you have more than two sets of data which you'd like to keep, and because there are only two rUser arrays, then you can access other macro arrays. This includes rArea, rMean, rStdDev, rX, rY, rMin, rMax, rLength, rMajor, rMinor, and rAngle. However you will need to be careful because these arrays are affected by the rCount value and you could write over your data. An example use of measurement arrays outside the intended use is a snipet of code from the Export look up table macro:
for i:=0 to 255 do begin
rArea[i+1]:=RedLut[i];
rMean[i+1]:=GreenLut[i];
rLength[i+1]:=BlueLut[i];
end;
Here rArea, rMean and rLength are used for Red, Green and Blue instead of area, mean and length.
If you have particular information, data, calculated results, or any type of numeric data which you want to keep, you can redirect it into the Results window. Use the SetUser label commands to title your field name. The rCount function keeps the current index of the measurement counter. Since rUser1 and rUser2 are arrays, you specify the index of the array with the rCount value. See below.
macro 'Count Black and White Pixels [B]';
{
Counts the number of black and white pixels in the current
selection and stores the counts in the User1 and User2 columns.
}
begin
RequiresVersion(1.44);
SetUser1Label('Black');
SetUser2Label('White');
Measure;
rUser1[rCount]:=histogram[255];
rUser2[rCount]:=histogram[0];
UpdateResults;
end;
You can also save data from the macro, to a tab delimeted text file by adding several
commands in your macro:
SetExport('Measurements');
Export('YourFileName');
By using a loop (for i:= 1 to nSlices) you can operate on a series of 2D images. The nSlices function returns the number of slices in the stack.
macro 'Reduce Noise';
var
i:integer;
begin
if nSlices=0 then begin
PutMessage('This window is not a stack');
exit;
end;
for i:= 1 to nSlices do begin
SelectSlice(i);
ReduceNoise; {Call any routine you want, including UserCode}
end;
end;
See the series of stack macros distributed with the Image program for more examples.
From reply of Doug Morris <dmorris@bmrl.med.uiuc.edu> on nih-image@soils.umn.edu
> I have a question about how to "extract" a substring from a string using
> NIH Image macro language. It doesn't seem to have pascal's "copy(source,
> index, count)" function implemented at macro language level.
It is possible to work around this particular problem. Below is an example macro that will allow you to pull a substring out of a string.Just cut it out of the mail message and read into image.
{
An example routine to return a substring from a string in NIH Image macro.
}
var
ReturnString:string;
procedure copystring(SourceString:string,index:integer,count:integer);
begin;
ReturnString:=SourceString;
if index > 0 then Delete(ReturnString,0,index);
Delete(ReturnString,count+1,length(ReturnString)-count);
end;
macro 'test copystring'
var
TestString:string;
begin
TestString:='This is a test';
copystring(TestString,11,4);
PutMessage('The Returned String is : ' ReturnString);
end;